// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;

namespace SnapObjects.Data
{
    internal class EntryStatefulModel<TModel> : StatefulModel<TModel>, IUpdateSplitable
    {
        private readonly IModelEntry<TModel> _modelEntry;

        public EntryStatefulModel(
            ICompositeModelAccessor<TModel> compositeModelAccessor,
            IModelEntry<TModel> modelEntry,
            ChangeTrackingStrategy changeTrackingStrategy)
            : this(compositeModelAccessor, compositeModelAccessor.ModelFactory, modelEntry, changeTrackingStrategy)
        {
        }

        public EntryStatefulModel(
            IModelFactory<TModel> modelFactory,
            IModelEntry<TModel> modelEntry,
            ChangeTrackingStrategy changeTrackingStrategy)
            : this(null, modelFactory, modelEntry, changeTrackingStrategy)
        {
        }

        public override void SetIdentityValue(string name, object value)
        {
            base.SetIdentityValue(name, value);

            _modelEntry.SetCurrentValue(name, value);
        }

        public IStatefulModel SplitDeletedFromUpdate(ChangeTrackingStrategy changeTrackingStrategy)
        {
            return this.CreateStatefulModel(
                _originalModel, ModelState.Deleted, changeTrackingStrategy);
        }

        public IStatefulModel SplitInsertedFromUpdate(ChangeTrackingStrategy changeTrackingStrategy)
        {
            return this.CreateStatefulModel(
                _currentModel, ModelState.NewModified, changeTrackingStrategy);
        }

        private IStatefulModel CreateStatefulModel(
            TModel model,
            ModelState modelState,
            ChangeTrackingStrategy changeTrackingStrategy)
        {
            if (this.ModelState == ModelState.Modified)
            {
                return new PocoStatefulModel<TModel>(
                    _modelFactory, model, modelState, changeTrackingStrategy);
            }

            throw new InvalidOperationException(Properties.Resource.DELTHENINS_MODELSTATE_INVALID);
        }

        private EntryStatefulModel(
            ICompositeModelAccessor<TModel> compositeModelAccessor,
            IModelFactory<TModel> modelFactory,
            IModelEntry<TModel> modelEntry,
            ChangeTrackingStrategy changeTrackingStrategy)
            : base(compositeModelAccessor, modelFactory, modelEntry.ModelState, changeTrackingStrategy)
        {
            modelEntry.CurrentValueChanged += this.SetCurrentValue;

            _modelEntry = modelEntry;

            if (modelEntry.ModelState == ModelState.NewModified)
            {
                foreach (var propertyName in modelEntry.Properties)
                {
                    var propertyState = modelEntry.GetPropertyState(propertyName);

                    if (propertyState == PropertyState.Modified)
                    {
                        this.SetModelEntryData(propertyName, modelEntry);
                    }
                }
            }
            else
            {
                foreach (var propertyName in modelEntry.Properties)
                {
                    this.SetModelEntryData(propertyName, modelEntry);
                }
            }
        }

        private void SetModelEntryData(string propertyName, IModelEntry<TModel> modelEntry)
        {
            var modelMeta = _accessor.ModelMeta;

            if (modelMeta.PropertyMap.TryGetValue(propertyName, out var property))
            {
                var currentValue = modelEntry.GetCurrentValue(property.Name);

                var originalValue = modelEntry.GetOriginalValue(property.Name);

                _accessor.SetValue(_currentModel, property.Name, currentValue);

                _accessor.SetValue(_originalModel, property.Name, originalValue);

                this.SetPropertyState(property.Index, modelEntry.GetPropertyState(property.Name));
            }
        }
    }
}
